/*
 * FNISVGEditor - JavaScript - based editor for SVG graphics 
 * Copyright (C) 2008 - 2009 Antti Leppä / Foyt
 * http://www.foyt.fi
 * 
 * License: 
 * 
 * Licensed under GNU Lesser General Public License Version 2.1 or later (the "LGPL") 
 * http://www.gnu.org/licenses/lgpl.html
 */
FNISVGEditorPathEditPlugin = Class.create(FNISVGEditorPlugin, {
  initialize: function($super, editorInstance, pluginURL){
    $super(editorInstance, pluginURL);
    
    var button = new FNIToggleableButton({});
    button.setImage(this.getPluginURL() + "/gfx/icons/22x22/actions/node-edit.png");    
    button.addListener('buttonUp', this, this._onButtonUp);
    button.addListener('buttonDown', this, this._onButtonDown);
    editorInstance.registerToolbarItem('editPath', button);
    button.setTooltip(SVGLocale.getText("pathEditPlugin_buttonTooltip"));
    
    this._handles = new Array();
    this._selectedHandles = null;
    editorInstance.getUndoManager().addListener("statusChanged", this, this._onUndoManagerStatusChanged);
  },
  getName: function(){
    return "pathedit";
  },
  setMode: function(mode){
    this._previousMode = this._mode;
    this._mode = mode;
    
  },
  getMode: function(){
    return this._mode;
  },
  setPreviousMode: function () {
    this.setMode(this._previousMode);
  },
  _onButtonUp: function(event){
    this._stopListening();
    this._removeHandlers();
  },
  _onButtonDown: function(event){
    this._startListening();
    this.setMode(FNISVGEditorPathEditPlugin.MODE_NONE);
    this.getEditorInstance().getSelectionManager().setIconset('none');
    this.getEditorInstance().getSelectionManager().setMode(FNISelectionManager.MODE_SELECT);
    this._redeployHandles();
  },
  _redeployHandles: function(){
    this._removeHandlers();
    
    this._leverLayer = this._editorInstance.getSVGDocument().createElement("g");
    this._editorInstance.getSVGDocument().appendTemporaryElement(this._leverLayer);
    this._handleLayer = this._editorInstance.getSVGDocument().createElement("g");
    this._editorInstance.getSVGDocument().appendTemporaryElement(this._handleLayer);
    
    if (this.getEditorInstance().getSelectionManager().hasSelections()) {
      var element = this.getEditorInstance().getSelectionManager().getSelectedElements()[0];
      
      if (element.tagName == 'path') {
        var pathController = getSVGElementControllerVault().getElementController("path");
        var segments = pathController.getSegments(element);
        var segmentHandles = new Array();
        
        for (var i = 0; i < segments.length; i++) {
          var segment = segments[i];
          var segmentParams = segment.getParameters();
          
          if (segmentParams.length > 0) {
            var segmentHandle = new FNISVGEditorPathEditPlugin_SegmentHandle(this.getEditorInstance(), element, segmentParams[segmentParams.length - 1]);
            segmentHandle.addListener("mouseover", this, this._onHandleMouseOver);
            segmentHandle.addListener("mouseout", this, this._onHandleMouseOut);
            segmentHandle.addListener("mousedown", this, this._onHandleMouseDown);
            segmentHandles[i] = segmentHandle;
            segmentHandle.deploy(this._handleLayer);
            this._handles.push(segmentHandle);
          }
        }
        
        for (var i = 0; i < segments.length; i++) {
          var segment = segments[i];
          var segmentParams = segment.getParameters();
          var next = -1;
          
          for (var j = 0; j < segmentParams.length - 1; j++) {
            var connectedHandle = segmentHandles[i + next];
            var leverHandle = new FNISVGEditorPathEditPlugin_SegmentLeverHandle(this.getEditorInstance(), element, segmentParams[j], connectedHandle);
            leverHandle.addListener("mouseover", this, this._onHandleMouseOver);
            leverHandle.addListener("mouseout", this, this._onHandleMouseOut);
            leverHandle.addListener("mousedown", this, this._onHandleMouseDown);
            next = (next == -1) ? 0 : -1;
            connectedHandle.addConnectedLeverHandle(leverHandle);
            leverHandle.deploy(this._handleLayer, this._leverLayer);
            this._handles.push(leverHandle);
          }
        }
      }
    }
  },
  _startListening: function(){
    this.getEditorInstance().addListener("mousedragstart", this, this._onEditorMouseDragStart);
    this.getEditorInstance().addListener("mousedragend", this, this._onEditorMouseDragEnd);
    this.getEditorInstance().addListener("mousedragging", this, this._onEditorMouseDragging);
    this.getEditorInstance().addListener("mousemove", this, this._onEditorMouseMove);
    this.getEditorInstance().addListener("mouseup", this, this._onEditorMouseUp);
    this.getEditorInstance().getSelectionManager().addListener("selectionChanged", this, this._onSelectionChanged);
    
  },
  _stopListening: function(){
    this.getEditorInstance().removeListener("mousedragstart", this);
    this.getEditorInstance().removeListener("mousedragend", this);
    this.getEditorInstance().removeListener("mousedragging", this);
    this.getEditorInstance().removeListener("mousemove", this);
    this.getEditorInstance().removeListener("mouseup", this);
    this.getEditorInstance().getSelectionManager().removeListener("selectionChanged", this);    
  },
  _onHandleMouseOver: function(event){
    if (event.handle.isSelected() == false)
      event.handle.highlight();
  },
  _onHandleMouseOut: function(event){
    if (event.handle.isSelected() == false)
      event.handle.unhighlight();
  },
  _onHandleMouseDown: function(event){
    switch (this.getMode()) {
      case FNISVGEditorPathEditPlugin.MODE_NONE:
        if (event.handle.isSelected() != true)
          this._setSelectedHandles([event.handle]);
        
        this.setMode(FNISVGEditorPathEditPlugin.MODE_MOVINGHANDLE);
        this.getEditorInstance().getSelectionManager().setMode(FNISelectionManager.MODE_NONE);
        var element = this.getEditorInstance().getSelectionManager().getSelectedElements()[0];
        this.getEditorInstance().getUndoManager().addUndoableEvent(new FNISVGEditorUndoablePathChangedEvent(element, element.getAttribute('d')));

      break;
    }
  },
  _onSelectionChanged: function(event){
    this._redeployHandles();
  },
  _removeHandlers: function(){
    if (this._leverLayer != null) {
      this._editorInstance.getSVGDocument().removeTemporaryElement(this._leverLayer);
      this._leverLayer = null;  
    }
        
    if (this._handleLayer != null) {
      this._editorInstance.getSVGDocument().removeTemporaryElement(this._handleLayer);      
      this._handleLayer = null;
    }
  },
  _selectHandlesWithin: function(sx1, sy1, sx2, sy2){
    if (sx2 < sx1) {
      var t = sx2;
      sx2 = sx1;
      sx1 = t;
    }
    
    if (sy2 < sy1) {
      var t = sy2;
      sy2 = y1;
      sy1 = t;
    }
    
    var handles = new Array();
    
    for (var i = 0; i < this._handles.length; i++) {
      var pos = this._handles[i].getPosition();
      if ((sx1 <= pos.x) && (sx2 >= pos.x) && (sy1 <= pos.y) && (sy2 >= pos.y))
        handles.push(this._handles[i]);       
    }
    
    this._setSelectedHandles(handles);
  },
  _setSelectedHandles: function (handles) {
    if (this._selectedHandles != null) {
      while (this._selectedHandles.length > 0)
        this._selectedHandles.pop().deselect();     
    } else 
      this._selectedHandles = new Array();
     
    for (var i = 0; i < handles.length; i++) {
      var handle = handles[i];
      handle.select();
      this._selectedHandles.push(handle);
    }
  },
  _onEditorMouseMove: function(event){
    switch (this.getMode()) {
      case FNISVGEditorPathEditPlugin.MODE_MOVINGHANDLE:
        var moveX = event.mouseEditorX - event.mouseLastX;
        var moveY = event.mouseEditorY - event.mouseLastY;
        
        if ((this._selectedHandles != null) && (this._selectedHandles.length > 0)) {
          for (var i = 0; i < this._selectedHandles.length; i++)            
            this._selectedHandles[i].moveBy(moveX, moveY);
            
          this.getEditorInstance().getSelectionManager().updateSelections();                      
        }
      break;
    }
  },
  _onEditorMouseUp: function(event){
    switch (this.getMode()) {
      case FNISVGEditorPathEditPlugin.MODE_MOVINGHANDLE:
        this.setMode(FNISVGEditorPathEditPlugin.MODE_NONE);
        this.getEditorInstance().getSelectionManager().setPreviousMode();
      break;
    }
  },
  _onEditorMouseDragStart: function(event){
    switch (this.getMode()) {
      case FNISVGEditorPathEditPlugin.MODE_NONE:
        this._moveSelectionBox(event.mouseEditorX, event.mouseEditorY, 0, 0);
        this.setMode(FNISelectionManager.MODE_SELECTING);
      break;
    }
  },
  _onEditorMouseDragEnd: function(event){
    switch (this.getMode()) {
      case FNISVGEditorPathEditPlugin.MODE_SELECTING:
        var rPos = this._selectionBox.getBBox();
        this._selectHandlesWithin(rPos.x, rPos.y, rPos.x + rPos.width, rPos.y + rPos.height);
        this.setPreviousMode();
        this._hideSelectionBox();
      break;
      case FNISelectionManager.MODE_DRAGGING_ICON:
        this.setPreviousMode();
      break;
    }
  },
  _onEditorMouseDragging: function(event){
    switch (this.getMode()) {
      case FNISVGEditorPathEditPlugin.MODE_SELECTING:
        var width = event.mouseEditorX - event.dragStartX;
        var height = event.mouseEditorY - event.dragStartY;
        var x = event.dragStartX;
        var y = event.dragStartY;
        
        if (width < 0) {
          width = -width;
          x -= width;
        }
        
        if (height < 0) {
          height = -height;
          y -= height;
        }
        
        this._moveSelectionBox(x, y, width, height);
      break;
    }
  },
  _moveSelectionBox: function(x, y, width, height){
    if (!this._selectionBox) {
      this._selectionBox = this._editorInstance.getSVGDocument().createElement("rect");
      this._selectionBox.setAttribute('style', this._editorInstance.getConfigurationManager().getValue("selectionRectStyle"));
      this._editorInstance.getSVGDocument().appendTemporaryElement(this._selectionBox);
    }
    
    var controller = getSVGElementControllerVault().getElementControllerFor(this._selectionBox);
    controller.moveTo(this._selectionBox, x, y);
    controller.resizeTo(this._selectionBox, width, height);
  },
  _hideSelectionBox: function(){
    if (this._selectionBox != null) {
      this._editorInstance.getSVGDocument().removeTemporaryElement(this._selectionBox);
      this._selectionBox = null;
    }
  },
  _onUndoManagerStatusChanged: function (event) {
    if ((event.action == 'undo')||(event.action == 'redo')) {
      this._redeployHandles();
    } 
  }
});

FNISVGEditorPathEditPlugin_AbstractSegmentHandle = Class.create({
  initialize: function(editorInstance, pathElement, segmentPos, elementConf){
    this._editorInstance = editorInstance;
    this._handleElement = fi.foyt.svg.svgdom.FNISVGDOMUtils.createElementFromJSON(this._editorInstance.getSVGDocument(), elementConf);
    this._pathElement = pathElement;
    this._segmentPos = segmentPos;
    this._selected = false;

    var _this = this;
    this._handleElement.addEventListener("mouseover", function (event) {
      _this._onMouseOver(event);
    }, false);
    
    this._handleElement.addEventListener("mouseout", function (event) {
      _this._onMouseOut(event);
    }, false);
    
    this._handleElement.addEventListener("mousedown", function (event) {
      _this._onMouseDown(event);
    }, false);
    
    this._pathController = getSVGElementControllerVault().getElementController("path");
  },
  getPosition: function(){
    return this._segmentPos;
  },
  _onMouseOver: function(event){
    this.fire("mouseover", {
      handle: this
    });
  },
  _onMouseOut: function(event){
    this.fire("mouseout", {
      handle: this
    });
  },
  _onMouseDown: function(event){
    this.fire("mousedown", {
      handle: this
    });
  },
  moveTo: function(x, y){
    this._segmentPos.x = x;
    this._segmentPos.y = y;
    this._pathController.markPathChanged(this._pathElement);
    this._pathController.updatePath(this._pathElement);
    this.refresh();
  },
  moveBy: function (x, y) {
    this.moveTo(this._segmentPos.x + x, this._segmentPos.y + y);
  },
  select: function () {
    this._selected = true;
    this.highlight();
  },
  deselect: function(){
    this._selected = false;    
    this.unhighlight();   
  },
  unhighlight: function () {
    if (this._highlighted == true) {
      FNISVGEditorElementStylingUtils.removeTemporaryStyle(this._handleElement);
      this._highlighted = false;
    }
  },
  isSelected: function () {
    return this._selected;
  } 
});

Object.extend(FNISVGEditorPathEditPlugin_AbstractSegmentHandle.prototype, fni.events.FNIEventSupport);

FNISVGEditorPathEditPlugin_SegmentHandle = Class.create(FNISVGEditorPathEditPlugin_AbstractSegmentHandle, {
  initialize: function($super, editorInstance, pathElement, segmentPos){
    $super(editorInstance, pathElement, segmentPos, editorInstance.getConfigurationManager().getValue("plugins/pathedit/segmentHandle"));
    
    var segmentHandleStyle = this._editorInstance.getConfigurationManager().getValue("plugins/pathedit/segmentHandleStyle");
    FNISVGEditorElementStylingUtils.setStyles(this._handleElement, segmentHandleStyle);
    this._connectedLeverHandles = new Array();
  },
  deploy: function(handleLayer){
    handleLayer.appendChild(this._handleElement);
    this._handleBBox = this._handleElement.getBBox();
    this.refresh();
  },
  refresh: function(){
    var controller = getSVGElementControllerVault().getElementControllerFor(this._handleElement);
    controller.moveTo(this._handleElement, this._segmentPos.x - (this._handleBBox.width / 2), this._segmentPos.y - (this._handleBBox.height / 2));
    
    for (var i = 0; i < this._connectedLeverHandles.length; i++) 
      this._connectedLeverHandles[i].refresh();
  },
  getElement: function(){
    return this._handleElement;
  },
  addConnectedLeverHandle: function(connectedLeverHandle){
    this._connectedLeverHandles.push(connectedLeverHandle);
  },
  highlight: function(){
    if (this._highlighted != true) {
      var style = this._editorInstance.getConfigurationManager().getValue("plugins/pathedit/activeSegmentHandleStyle");
      FNISVGEditorElementStylingUtils.setTemporaryStyle(this._handleElement, style);
      this._highlighted = true;
    }
  }
});

FNISVGEditorPathEditPlugin_SegmentLeverHandle = Class.create(FNISVGEditorPathEditPlugin_AbstractSegmentHandle, {
  initialize: function($super, editorInstance, pathElement, segmentPos, connectedHandle){
    $super(editorInstance, pathElement, segmentPos, editorInstance.getConfigurationManager().getValue("plugins/pathedit/leverHandle"));
    this._connectedHandle = connectedHandle;
    this._leverElement = this._editorInstance.getSVGDocument().createElement("line");
    
    var leverStyle = this._editorInstance.getConfigurationManager().getValue("plugins/pathedit/leverStyle");
    var leverHandleStyle = this._editorInstance.getConfigurationManager().getValue("plugins/pathedit/leverHandleStyle");
    
    FNISVGEditorElementStylingUtils.setStyles(this._leverElement, leverStyle);
    FNISVGEditorElementStylingUtils.setStyles(this._handleElement, leverHandleStyle);
  },
  deploy: function(handleLayer, leverLayer){
    handleLayer.appendChild(this._handleElement);
    leverLayer.appendChild(this._leverElement);
    this._handleBBox = this._handleElement.getBBox();
    this.refresh();
  },
  refresh: function(){
    var handleController = getSVGElementControllerVault().getElementControllerFor(this._handleElement);
    var leverController = getSVGElementControllerVault().getElementControllerFor(this._leverElement);
    var connectedHandlePos = this._connectedHandle.getPosition();
    
    handleController.moveTo(this._handleElement, this._segmentPos.x - (this._handleBBox.width / 2), this._segmentPos.y - (this._handleBBox.height / 2));
    leverController.setLine(this._leverElement, this._segmentPos.x, this._segmentPos.y, connectedHandlePos.x, connectedHandlePos.y);
  },
  getElement: function(){
    return this._handleElement;
  },
  highlight: function(){
    if (this._highlighted != true) {
      var style = this._editorInstance.getConfigurationManager().getValue("plugins/pathedit/activeLeverHandleStyle");
      FNISVGEditorElementStylingUtils.setTemporaryStyle(this._handleElement, style);
      this._highlighted = true;
    }
  }
});


Object.extend(FNISVGEditorPathEditPlugin, {
  MODE_NONE: 'none',
  MODE_OVERHANDLE: 'overhandle',
  MODE_SELECTING: 'selecting',
  MODE_MOVINGHANDLE: 'movinghandle'
});

document.fire("svgEditorPluginLoaded:pathedit", {
  pluginClass: FNISVGEditorPathEditPlugin
});
